/**
 * 
 */
package sw.utils;

import static org.jocl.CL.clGetDeviceIDs;
import static org.jocl.CL.clGetDeviceInfo;
import static org.jocl.CL.clGetPlatformIDs;
import static org.jocl.CL.clGetPlatformInfo;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;

import org.jocl.CL;
import org.jocl.Pointer;
import org.jocl.Sizeof;
import org.jocl.cl_device_id;
import org.jocl.cl_event;
import org.jocl.cl_platform_id;

/**
 * @author Chung
 *
 */
public class JOCLUtils {
	
	/**
     * Returns the value of the device info parameter with the given name
     *
     * @param device The device
     * @param paramName The parameter name
     * @return The value
     */
    public static String getString(cl_device_id device, int paramName) {
        long size[] = new long[1];
        clGetDeviceInfo(device, paramName, 0, null, size);
        byte buffer[] = new byte[(int)size[0]];
        clGetDeviceInfo(device, paramName, 
            buffer.length, Pointer.to(buffer), null);
        return new String(buffer, 0, buffer.length-1);
        
    }
    
    /**
     * Returns the value of the platform info parameter with the given name
     *
     * @param platform The platform
     * @param paramName The parameter name
     * @return The value
     */
    public static String getString(cl_platform_id platform, int paramName)
    {
        long size[] = new long[1];
        clGetPlatformInfo(platform, paramName, 0, null, size);
        byte buffer[] = new byte[(int)size[0]];
        clGetPlatformInfo(platform, paramName, 
            buffer.length, Pointer.to(buffer), null);
        return new String(buffer, 0, buffer.length-1);
    }
    
    /**
     * Compute the execution duration of the given event, in milliseconds
     * 
     * @param event The event
     * @return The execution duration, in milliseconds
     */
    public static float computeDurationMs(cl_event event)
    {
        long startTime[] = {0};
        long endTime[] = {0};
        CL.clGetEventProfilingInfo(
            event, CL.CL_PROFILING_COMMAND_START,
            Sizeof.cl_ulong, Pointer.to(startTime), null);
        CL.clGetEventProfilingInfo(
            event, CL.CL_PROFILING_COMMAND_END,
            Sizeof.cl_ulong, Pointer.to(endTime), null);
        long durationNs = endTime[0]-startTime[0];
        return durationNs / 1e6f;
    }
    
    public static cl_device_id[] getDevices(cl_platform_id platform, long deviceType ) {
    	int numDevicesArray[] = new int[1];
	    clGetDeviceIDs(platform, deviceType, 0, null, numDevicesArray);	   
		int numDevices = numDevicesArray[0];
		
		 // Obtain a device IDs 
        cl_device_id devices[] = new cl_device_id[numDevices];
        clGetDeviceIDs(platform, deviceType, numDevices, devices, null);
        return devices;
    }
    
    public static String generateSequenceStringWithLength(Integer length){
    	
        Random rand = new Random();        
        StringBuffer key = new StringBuffer();
        if (length == null || length <= 0){
        	length = 16;
        }
        
        for (int i = 0; i < length; i++)
        {
            rand(8, 25, rand);
            key.append(generateRandomChar(rand));
        }
        
        return key.toString().toUpperCase();
    	
    }
    
    private static int rand(int lo, int hi, Random rand) {
        int n = hi - lo + 1;
        int i = rand.nextInt() % n;
        if (i < 0) i = -i;
        return lo + i;
    }
    
    private static char generateRandomChar(Random r) {
        return (char) rand('a', 'z', r);
    }
    
    /**
     * Load content of kernel file and store in String
     * @param filePath
     * @return
     * @throws IOException
     */
    public static String loadKernel(String filePath) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader(filePath));
		try {
			StringBuilder sb = new StringBuilder();
			String line = br.readLine();

			while (line != null) {
				sb.append(line);
				sb.append("\n");
				line = br.readLine();
			}
			return sb.toString();
		} finally {
			br.close();
		}
    }/**
	 * Get greatest divisor between a and b and it can not equal a or b
	 * @param a
	 * @param b
	 * @return
	 */
	public static int getGreatestDivisor(int a, int b) {
		int i = 0;
		int min = Math.min(a, b);
		int maxSubSize = 500;
		for (i = a; i > 0; i--) {
			if (a % i == 0 && b % i == 0 && i !=a && i != b && i < min && i < maxSubSize) {
				break;
			}
		}
		return i;
	}
    
	public static int min(int a, int b, int c) {
		return Math.min(a, Math.min(b, c));
	}

	/**
	 *  Obtain the number of platforms
	 * @return
	 */
	public static cl_platform_id[] getPlatforms() {
		int numPlatformsArray[] = new int[1];
		clGetPlatformIDs(0, null, numPlatformsArray);
		int numPlatforms = numPlatformsArray[0];
		
		cl_platform_id platforms[] = new cl_platform_id[numPlatforms];
		clGetPlatformIDs(platforms.length, platforms, null);
		return platforms;
	}
	
	/**
	 * Get all devices the same platform
	 * @param platform
	 * @return
	 */
	public static cl_device_id[] getDevices(cl_platform_id platform) {
		 // Obtain the number of devices for the platform
		int numDevicesArray[] = new int[1];
		clGetDeviceIDs(platform, CL.CL_DEVICE_TYPE_ALL, 0, null, numDevicesArray);
		int numDevices = numDevicesArray[0];
       
        // Obtain a device IDs 
		cl_device_id devices[] = new cl_device_id[numDevices];
		clGetDeviceIDs(platform, CL.CL_DEVICE_TYPE_ALL, numDevices, devices, null);
		return devices;
	}

	/**
	 * Get max work group size of device
	 * @param device
	 * @return
	 */
	public static long getMaxWorkGroupSize(cl_device_id device) {
        ByteBuffer buffer = ByteBuffer.allocate(Sizeof.size_t).order(ByteOrder.nativeOrder());
        clGetDeviceInfo(device, CL.CL_DEVICE_MAX_WORK_GROUP_SIZE , Sizeof.size_t, Pointer.to(buffer), null);
        return buffer.getLong();
    }
	
	public static boolean isPassed(int[] openCLMatrix, int[][] serialMatrix) {
		int row = serialMatrix[0].length;
		int col = serialMatrix.length;
		for (int i = 0; i < row; i++) {
			for (int j = 0; j < col; j++)
			if ( serialMatrix[i][j] != openCLMatrix[i*col + j]) {
				return false;
			}
		}
		return true;
	}
}
